/*
 * Copyright (C) 2008 Alessandro Sivieri <alessandro.sivieri@chimera-bellerofonte.eu>.
 *
 *  This file is part of Daex.
 *
 *  Daex is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Foobar is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.chimera_bellerofonte.tesi.datatypes;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.logging.Level;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

import eu.chimera_bellerofonte.tesi.DaexIcon;
import eu.chimera_bellerofonte.tesi.DaexLogger;
import eu.chimera_bellerofonte.tesi.DaexProperties;


/**
 * Questa classe definisce un'implementazione della lettura dei dettagli di un DVD Video; si appoggia a funzioni native che utilizzano libdvdread tipicamente su piattaforma Unix.
 * @see eu.chimera_bellerofonte.tesi.datatypes.Dvd
 * @author  Sivieri Alessandro
 * @author  Vitucci Nicola
 * @version  20/apr/07
 */
public class DvdNative implements Dvd
{
	static final long serialVersionUID = 1L;

	private String device=null;
	private int titles=0;
	private int mainTitle=-1;
	private int mainTitleChapters=0;
	private String hash=null;

	static
	{
		try
		{
			System.loadLibrary("dvdwrapper");
			DaexLogger.getDaexLogger().info("Libreria correttamente inizializzata");
			if(!DaexProperties.isProperty("device"))
			{
				DaexProperties.addProperty("device", getDefaultDevice());
			}
		}
		catch (Exception e)
		{
			DaexLogger.getDaexLogger().log(Level.SEVERE, "Impossibile caricare la libreria per i metodi nativi", e);
			JOptionPane.showMessageDialog(null,
					"Impossibile caricare la libreria di gestione del lettore DVD: "+e.getLocalizedMessage(),
					"Errore di caricamento libreria esterna",
					JOptionPane.ERROR_MESSAGE, new ImageIcon(DvdNative.class.getResource(DaexIcon.ERROR)));
			System.exit(1);
		} 
	}
	
	private void init() throws DvdException
	{
		this.mainTitleChapters=getDvdTitleChapters(device, mainTitle);
		this.titles=getDvdTitles(device);
		this.hash=getDvdHash(device);
		DaexLogger.getDaexLogger().info("Inizializzazione del DVD effettuata utilizzando il device "+device+", il titolo "+mainTitle+"; il DVD ha hash "+hash);
	}
	
	/**
	 * Costruttore; setta gli attributi di classe con i
	 * valori specificati.
	 * 
	 * @throws DvdException eccezione lanciata in caso di problemi di lettura del device
	 */
	public DvdNative() throws DvdException
	{
		this.device=DaexProperties.getProperty("device");
		this.mainTitle=getDvdMainTitle(device);
		init();
	}

	/**
	 * Costruttore; setta gli attributi di classe con i
	 * valori specificati.
	 * 
	 * @param device il device specificato, differente da quello di default
	 * @throws DvdException  eccezione lanciata in caso di problemi di lettura del device
	 */
	public DvdNative(String device) throws DvdException
	{
		this.device = device;
		this.mainTitle=getDvdMainTitle(device);
		init();
	}

	/**
	 * Costruttore; setta gli attributi di classe con i
	 * valori specificati.
	 * 
	 * @param device il device da utilizzare al posto di quello di default
	 * @param mainTitle il titolo da cui eseguire il rip delle tracce
	 * @throws DvdException  eccezione lanciata in caso di problemi di lettura del device
	 */
	public DvdNative(String device, int mainTitle) throws DvdException
	{
		this.device = device;
		this.mainTitle = mainTitle;
		init();
	}

	/**
	 * Costruttore; setta gli attributi di classe con i
	 * valori specificati.
	 * 
	 * @param device il device da leggere
	 * @param hash parametro salvato da cui controllare la corrispondenza
	 * @throws DvdException eccezione lanciata in caso di problemi con il dvd
	 * @throws DvdCorrispondenceException eccezione lanciata se il dvd non corrisponde
	 */
	public DvdNative(String device, String hash) throws DvdException, DvdCorrispondenceException
	{
		if(!hash.equals(getDvdHash(device)))
		{
			throw new DvdCorrispondenceException();
		}
		this.device = device;
		this.hash = hash;
		init();
	}

	/**
	 * Costruttore; setta gli attributi di classe con i
	 * valori specificati.
	 * 
	 * @param device il device da leggere
	 * @param mainTitle il titolo da considerare
	 * @param hash il parametro da verificare per garantire la corrispondenza con le informazioni salvate
	 * @throws DvdCorrispondenceException eccezione lanciata se non vi e' la corrispondenza tra l'hash specificato ed il dvd in lettura
	 * @throws DvdException eccezione lanciata in caso di problemi con il dvd
	 */
	public DvdNative(String device, int mainTitle, String hash) throws DvdCorrispondenceException, DvdException
	{
		if(!hash.equals(getDvdHash(device)))
		{
			throw new DvdCorrispondenceException();
		}
		this.device = device;
		this.mainTitle = mainTitle;
		this.hash = hash;
		init();
	}

	/**
	 * Questo metodo ritorna il valore attuale di device.
	 * @return  il valore di device
	 * @uml.property  name="device"
	 */
	public String getDevice()
	{
		return device;
	}

	/**
	 * @see  eu.chimera_bellerofonte.tesi.datatypes.Dvd#getMainTitle()
	 * @uml.property  name="mainTitle"
	 */
	public int getMainTitle()
	{
		return mainTitle;
	}
	
	/**
	 * @see eu.chimera_bellerofonte.tesi.datatypes.Dvd#setMainTitle(int)
	 */
	public void setMainTitle(int title) throws DvdException
	{
		this.mainTitle=title;
		init();
	}

	/**
	 * @see  eu.chimera_bellerofonte.tesi.datatypes.Dvd#getMainTitleChapters()
	 * @uml.property  name="mainTitleChapters"
	 */
	public int getMainTitleChapters()
	{
		return mainTitleChapters;
	}

	/**
	 * @see  eu.chimera_bellerofonte.tesi.datatypes.Dvd#getTitles()
	 * @uml.property  name="titles"
	 */
	public int getTitles()
	{
		return titles;
	}

	/**
	 * @see  eu.chimera_bellerofonte.tesi.datatypes.Dvd#getHash()
	 * @uml.property  name="hash"
	 */
	public String getHash()
	{
		return hash;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return "Device in use: "+device+"\nDvd hash: "+hash+"\nTitles: "+titles+"\nMain title: "+mainTitle+"\nMain title chapters: "+mainTitleChapters;
	}

	private native String getDvdHash(String device) throws DvdException;
	
	private native int getDvdTitles(String device) throws DvdException;
	
	private native int getDvdMainTitle(String device) throws DvdException;
	
	private native int getDvdTitleChapters(String device, int title) throws DvdException;

	/**
	 * Metodo per ottenere tutte le unita' disco del sistema
	 * sottostante (se sistema Unix), visionando la directory
	 * dei devices ("/dev") ed estraendone tutte le voci
	 * compatibili con unita' disco (hd* per unita' PATA, sd*
	 * per unita' SCSI, SATA e PATA nelle nuove versioni del
	 * kernel).
	 * 
	 * @return un array con i path completi come stringhe
	 */
	public static ArrayList<String> getDiscDevices()
	{
		ArrayList<String> elements=new ArrayList<String>();
		File devdir=new File("/dev");
		if(devdir.isDirectory()) // Unix
		{
			File[] files=devdir.listFiles(new FilenameFilter()
			{

				public boolean accept(File dir, String name)
				{
					if(name.startsWith("hd") || name.startsWith("sd"))
					{
						return true;
					}
					return false;
				}
								
			});
			for(File f: files)
				elements.add(f.getAbsolutePath());
			if(new File("/dev/dvd").exists())
			{
				elements.add("/dev/dvd");
			}
			if(new File("/dev/cdrom").exists())
			{
				elements.add("/dev/cdrom");
			}
		}
		else // Windows
		{
			for(int i=65; i<=90; i++)
			{
				char c=(char) i;
				elements.add(c+":");
			}
		}
		return elements;
	}

	/**
	 * @return the defaultDevice
	 */
	public static String getDefaultDevice()
	{
		if(System.getProperty("os.name").contains("Windows"))
			return "E:"; // device a caso, il primo piu' probabile (nel caso ide)
		else
			return "/dev/dvd";
	}
}
